//###############
// FILE : SearchEngine.java
// WRITER : Lior Zimmerman liorz06 066462300
// WRITER : Ohad Dan ohad_dan 036767960
// EXERCISE : oop ex3 2008
// DESCRIPTION: The main search engine of the program
//###############
package oop.ex3.fileSearchModel.SearchEngine;
import java.io.File;
import java.util.ArrayList;
import java.io.IOException;

import oop.ex3.fileSearchModel.filters.*;
import oop.ex3.fileSearchModel.filters.Conditionable.STATUS;
import oop.ex3.fileSearchModel.filters.exceptions.*;
import oop.ex3.fileSearchModel.parser.Parser;
import oop.ex3.fileSearchModel.parser.exceptions.ParsingException;
import oop.ex3.fileSearchModel.util.exceptions.*;
/**
 * @author liorz06
 * @author ohad_dan
 *this class implements a search engine, and is responsible for evaluating 
 *each file for the given expression
 */
public class SearchEngine {
	//***********************************************************************
	//  Data members :                                                      *
	//***********************************************************************
	ArrayList<File> _okFiles; //A list for all checked files that are OK.
	ArrayList<Conditionable> _byteNodeList;
	ArrayList<Conditionable> _textNodeList;
	String _sourceDir;
	//***********************************************************************
	//  Methods :                                                           *
	//***********************************************************************
	/**
	 * Constructs a new search engine.
	 * @param sourceDir The root of the filters tree.
	 */
	public SearchEngine(String sourceDir){
		_okFiles = new ArrayList<File>();
		_sourceDir = sourceDir;		
	}
	
	/**
	 * Checks if the given file is valid by the set of filters.
	 * @param file The file to be checked.
	 * @return True if the file is valid, false otherwise. 
	 */
	private boolean checkFile(File file, Conditionable filter)
	throws IOException, FilterDataException, ReaderIOException{
	    filter.reset(file, _sourceDir);
		STATUS condition = filter.getCondition(); 
		if (condition == STATUS.UNKNOWN){
            //The decision can't be made without checking in file content
		condition = checkContent(filter);
	    }
		if (condition == STATUS.TRUE){
			return true;
		}
		else{
			return false;
		}
	}
	/**         
	 * Checks text and byte lists and set the value of all the nodes
	 * for the file being checked.
	 * Assumption - All nodes holds the relevant file(set by reset).
	 */
	private STATUS checkContent(Conditionable filter) throws IOException,
	FilterDataException, ReaderIOException{	
	    ArrayList<Filter> textList = getTextList(filter);
	    ArrayList<Filter> byteList = getByteList(filter);
	    if( textList != null && !textList.isEmpty()) {
    	    TextFileSearch textSearcher = new TextFileSearch( textList );
    	    while ( textSearcher.startSearch() ) {
    	        STATUS status = filter.getCondition();
    	        if ( status != STATUS.UNKNOWN ) {
                   textSearcher.close();
    	           return status;
    	        }
    	    }
    	    textSearcher.close();
	    }
	    if ( byteList != null && !byteList.isEmpty() ) {
    	    BinaryFileSearch byteSearcher = new BinaryFileSearch(byteList);
    	    while ( byteSearcher.startSearch() ) {
    	        STATUS status = filter.getCondition();
    	        if ( status != STATUS.UNKNOWN ) {
    	            byteSearcher.close();
    	            return status;
    	        }
    	    }
    	    byteSearcher.close();
	    }
	    return filter.getCondition();
	}

	/**
	 * Gets a filter list and returns which of the file under source fit.
	 * @param filterStr A string of filters and operator logic set.
	 * @return A list with all files that hold by the given filters.
	 * @throws ParsingException In case of error while parsing.
	 * @throws IOException In case of error while reading from file.
	 * @throws FilterException In case of an error in one of the filters.
	 * @throws ReaderIOException In case of error while reading.
	 */
	public ArrayList<String> search(String filterStr) throws 
	ParsingException, IOException, FilterException,ReaderIOException{
	    Conditionable expTree;
	    try{
	        Parser parser = new Parser(filterStr);
	        expTree = parser.parse();
	        
	    }
	    catch( ParsingException e ){
	        throw e;
	    }
		_okFiles.clear();
		searchSourceSubFileSystem(new File(_sourceDir), expTree);
		return returnStrArr(_okFiles);
	}
	/**
	 * Returns a list with all the files paths.
	 * @param list The files.
	 * @return All files paths.
	 * @throws IOException In case of IO exception.
	 */
	private ArrayList<String> returnStrArr( ArrayList<File> list ) 
	throws IOException{
		ArrayList<String> arr = new ArrayList<String>();
		if ( !list.isEmpty() ) {
			for( File file: list ){
				arr.add(file.getCanonicalPath());
			}
		}
		return arr;
		
	}
	/**
	 * Recursively build a list with all the valid files. 
	 * @param dir the current checked directory.
	 * @throws IOException In case of error while reading from file.
	 * @throws FilterDataException In case of an error in one of the
	 *  filter's data.
	 * @throws ReaderIOException In case of error while reading.
	 */
	private void searchSourceSubFileSystem(File dir, Conditionable filter) 
	throws IOException,FilterDataException, ReaderIOException{
	//Configured now to return directories
    //as well as files - check school solution.	   
		File[] fileList= dir.listFiles();
		if (fileList != null){
			for (int i=0; i<fileList.length;i++){
				if (checkFile(fileList[i], filter) &&
						fileList[i].isFile() ){
					_okFiles.add(fileList[i]);
				}
				if(fileList[i].isDirectory()){
					searchSourceSubFileSystem(fileList[i], filter);
				}
			}
		}
	}
	/**       
	 * Returns a list with all byte filters.
	 * @return A list with all byte filters.
	 */
	private ArrayList<Filter> getByteList( Conditionable filter ){
		return filter.getByteList();
	}
	/**
	 * Returns a list with all text filters.
	 * @return A list with all text filters.
	 */
	private ArrayList<Filter> getTextList( Conditionable filter ){
		return filter.getTextList();
	}
	
}
